/* Includes ------------------------------------------------------------------*/
#include "AP3216.h"
#include "gpio.h"
#include "oled.h"

/*----------------------------------------------------------------------------*/
/* Configure AP3216                                                             */
/*----------------------------------------------------------------------------*/

uint8_t ap_slave_addr = 0x3c;

void AP3216_sda_out(uint8_t out) {
  GPIO_InitTypeDef GPIO_InitStruct;

  HAL_GPIO_WritePin(ap_GPIO_Port, ap_sda_Pin, GPIO_PIN_SET);

  GPIO_InitStruct.Pin = ap_sda_Pin;
  if (out) {
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  } else {
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  }

  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(ap_GPIO_Port, &GPIO_InitStruct);
}

void AP3216_IIC_Start(void) {
  AP3216_SCL_Set();
  AP3216_SDA_Set();
  AP3216_SDA_Clr();
}

void AP3216_IIC_Stop(void) {
  AP3216_SCL_Set();
  AP3216_SDA_Clr();
  AP3216_SDA_Set();
}

void AP3216_IIC_Wait_Ack(void) {
  AP3216_SCL_Set();
  AP3216_SCL_Clr();
}

void AP3216_IIC_Ack(uint8_t ack) {
  AP3216_SCL_Clr();

  if (ack) {
    AP3216_SDA_Clr();
  } else {
    AP3216_SDA_Set();
  }

  AP3216_SCL_Set();
  AP3216_SCL_Clr();
}

void AP3216_Write_IIC_Byte(uint8_t IIC_Byte) {
  uint8_t i;

  AP3216_SCL_Clr();

  for (i = 0; i < 8; i++) {
    if (IIC_Byte & 0x80) {
      AP3216_SDA_Set();
    } else {
      AP3216_SDA_Clr();
    }
    IIC_Byte <<= 1;
    AP3216_SCL_Set();
    AP3216_SCL_Clr();
  }
}

uint8_t AP3216_Read_IIC_Byte(uint8_t ack) {
  uint8_t i, receive = 0;

  AP3216_sda_out(0);

  for (i = 0; i < 8; i++) {
    AP3216_SCL_Clr();
    __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
    __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
    AP3216_SCL_Set();

    receive <<= 1;

    if (AP3216_READ_SDA) {
      receive++;
    }
  }

  AP3216_sda_out(1);

  if (ack) {
    AP3216_IIC_Ack(1);
  } else {
    AP3216_IIC_Ack(0);
  }

  return receive;
}

void AP3216_Single_Write(uint8_t addr, uint8_t data) {
  AP3216_IIC_Start();
  AP3216_Write_IIC_Byte(ap_slave_addr);
  AP3216_IIC_Wait_Ack();
  AP3216_Write_IIC_Byte(addr);
  AP3216_IIC_Wait_Ack();
  AP3216_Write_IIC_Byte(data);
  AP3216_IIC_Wait_Ack();
  AP3216_IIC_Stop();
}

uint8_t AP3216_Single_Read(uint8_t addr) {
  uint8_t rec;

  AP3216_IIC_Start();
  AP3216_Write_IIC_Byte(ap_slave_addr);
  AP3216_IIC_Wait_Ack();
  AP3216_Write_IIC_Byte(addr);
  AP3216_IIC_Wait_Ack();

  AP3216_IIC_Start();
  AP3216_Write_IIC_Byte(ap_slave_addr + 1);
  AP3216_IIC_Wait_Ack();

  rec = AP3216_Read_IIC_Byte(0);
  AP3216_IIC_Stop();

  return rec;
}

void AP3216_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(ap_GPIO_Port, ap_scl_Pin | ap_sda_Pin, GPIO_PIN_SET);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = ap_scl_Pin | ap_sda_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(ap_GPIO_Port, &GPIO_InitStruct);

  // [xxxx011] ALS and PS+IR functions active
  AP3216_Single_Write(0x00, 0x03);

  // [00000101] 0000:PS/IR Integrated time select 1T, 01:PS gain x2, 01:PS interrupt is triggered after 2 conversion time
  AP3216_Single_Write(0x20, 0x05);
}

uint16_t AP3216_ALS(void) {
  uint16_t ALS;
  ALS = AP3216_Single_Read(0x0d);
  ALS <<= 8;
  ALS |= AP3216_Single_Read(0x0c);

  return ALS;
}

uint16_t AP3216_PS(void) {
  uint16_t PS;
  PS = AP3216_Single_Read(0x0f);
  PS <<= 8;
  PS |= AP3216_Single_Read(0x0e);

  return PS;
}

uint16_t AP3216_IR(void) {
  uint16_t IR;
  IR = AP3216_Single_Read(0x0b);
  IR <<= 8;
  IR |= AP3216_Single_Read(0x0a);

  return IR;
}
